package it.low.game.server;

import it.low.game.core.action.Action;
import it.low.game.core.action.Follow;
import it.low.game.core.action.Move;
import it.low.game.core.action.Patrol;
import it.low.game.core.element.ActiveElement;
import it.low.game.core.element.activity.Targetable;
import it.low.game.core.location.MapPoint;
import it.low.game.core.message.ElementState;
import it.low.game.core.message.PlayerEvent;
import it.low.game.server.persistence.ServerStoring;
import it.low.game.server.util.ServerLogManager;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;


public class ServerEngine {
	
	private boolean running = false;
	
	private static Map<Long, ActiveElement> activePlayers = new HashMap<Long, ActiveElement>();
	private static Map<Long, ActiveElement> activeBots = new HashMap<Long, ActiveElement>();
	
	public void init(){
		ServerStoring.init();
	}
	
	public void start(){
		running = true;
		//this.run();
	}
	
	public void run(){
		ServerLogManager.logMsg("call ServerEngine.run() ");
        long startTime = System.currentTimeMillis();
        long currTime = startTime;

        ServerLogManager.logMsg("call ServerEngine.run() Server.running = "+Server.running);
        ServerLogManager.logMsg("call ServerEngine.run() this.running = "+this.running);
        while (Server.running && this.running) {
            long elapsedTime = System.currentTimeMillis() - currTime;
            currTime += elapsedTime;

            // update
            this.update(elapsedTime);
                       
            try {
                Thread.sleep(5);
            }
            catch (InterruptedException ex) { }
        }
	}
	
	public static void update(long elapsedTime){
		// UPDATE PLAYERS ACTION
		for(ActiveElement activePlayer : activePlayers.values()){
			activePlayer.executeActions(elapsedTime);
		}
		// UPDATE BOT ACTION
		for(ActiveElement activeBot : activeBots.values()){			
			
			// AI SIMULATION
			if(activeBot.isDefaultAction()){
				Set<Targetable> targets = getCloserInRangePlayers(activeBot, ActiveElement.SIG_RANGE);
				if(targets==null || targets.isEmpty()){
					activeBot.executeActions(elapsedTime);
				}else{					
					Follow follow = new Follow(targets.iterator().next());
					activeBot.startNewAction(follow);
				}
			}else{
				activeBot.executeActions(elapsedTime);
			}			
		}		
	}
	
	public static void setPlayer(ActiveElement activeElementPlayer){
		activePlayers.put(activeElementPlayer.getElementId(),activeElementPlayer);
	}
	
	public static void removePlayer(long idPlayer){
		activePlayers.remove(idPlayer);
	}
	
	public static void setBot(ActiveElement activeElementBot){
		
		Patrol patrol = new Patrol(getRandomPoint(),getRandomPoint(),getRandomPoint());
		activeElementBot.setNewAction(0,patrol);
		activeElementBot.startAction(0);
		
		activeBots.put(activeElementBot.getElementId(),activeElementBot);
	}
	
	public static void processEvent(PlayerEvent playerEvent){
		long playerId = playerEvent.getPlayerId();
		Action.TYPE actionType = playerEvent.getActionType();
		MapPoint mapPoint = playerEvent.getMapPoint();
		ActiveElement activePlayer = activePlayers.get(playerId);
		Action action = null;
		switch (actionType){
			case MOVE :{
				action = new Move(mapPoint);
				break;
			}
		}		
		activePlayer.setNewAction(0,action);
		activePlayer.startAction(0);
	}
	
	public static ElementState getPlayerState(long playerId){
		return activePlayers.get(playerId).getElementState();
	}
	
	public static Set<ElementState> getElementStates(long playerId){
		Set<ElementState> playerElementStates = new HashSet<ElementState>();
		for(ActiveElement activeElement : activeBots.values()){
			playerElementStates.add(activeElement.getElementState());
		}
		return playerElementStates;
	}
	
	public static Set<Targetable> getInRangePlayers(ActiveElement activeElement, String property){
		HashSet<Targetable> InRangePlayers = new HashSet<Targetable>();
		double range = activeElement.getProperty(property);		
		if(activePlayers!=null){
			for(ActiveElement activePlayer : activePlayers.values()){
				double distance = activeElement.getMapPosition().distance(activePlayer.getMapPosition());
				if(distance < range){
					InRangePlayers.add(activePlayer);
				}
			}
		}
		return InRangePlayers;
	}
	
	public static Set<Targetable> getCloserInRangePlayers(ActiveElement activeElement, String property){
		HashSet<Targetable> CloserInRangePlayers = new HashSet<Targetable>();
		double range = activeElement.getProperty(ActiveElement.SIG_RANGE);		
		if(activePlayers!=null){
			for(ActiveElement activePlayer : activePlayers.values()){
				double distance = activeElement.getMapPosition().distance(activePlayer.getMapPosition());
				if(distance <= range){
					if(distance<range){
						range = distance;
						CloserInRangePlayers.clear();
					}
					CloserInRangePlayers.add(activePlayer);					
				}
			}
		}
		return CloserInRangePlayers;
	}
	
//	public static Set<ElementSolid> getCloseElements(ActiveElement activeElement){
//	HashSet<ElementSolid> closeElements = new HashSet<ElementSolid>();
//	for(MapActiveElement iMapActiveElement : mapActiveElements){
//		if(!activeElement.equals(iMapActiveElement)){
//			double distance = activeElement.getMapPosition().distance(iMapActiveElement.getMapPosition());
//			double maxDistance = activeElement.getMapArea().getWidth()/2 + iMapActiveElement.getMapArea().getWidth()/2;
//			if(distance <= maxDistance){
//				closeElements.add(iMapActiveElement);
//			}	
//		}
//	}
//	return closeElements;
//}

//public static boolean isCollision(ActiveElement activeElement){
//	boolean collision = false;
//	for(ElementSolid elementSolid : getCloseElements(activeElement)){
//		collision = activeElement.isClose(elementSolid);
//		if(collision) return true;
//	}
//	return collision;
//}
	
	public static MapPoint getRandomPoint(){
		double x = getNumber(1500);
		double y = getNumber(1500);
		return new MapPoint(x,y);
	}
	
	private static double getNumber(int max){
		return Math.random()*max;
	}
	
	
}
